#include "router.h"
#include <cppbase/cstring.h>
#include <fstream>
#include "config.h"
#include <cstring>
namespace Http
{
	Router::Router()
	{
	}
	std::string Router::detect_filetype(std::filesystem::path p)
	{
		if (p.empty())
			return std::string();
		auto ext = p.extension().string().substr(1);

		if (ext == "html")
			return "text/html";
		if (ext == "txt" || ext == "cpp")
			return "text/plain";
		if (ext == "gif")
			return "image/gif";
		if (ext == "png")
			return "image/png ";
		if (ext == "jpg" || ext == "jpeg")
			return "image/jpeg ";

		return "application/octet-stream";
	}
	/**
	 * url->file path. 
	 * sort=0:last position
	 * is_strict=true: full match,else start_with match.
	 * return true:success
	*/
	bool Router::add_handler(std::string url, std::string path, uint16_t sort, bool is_strict)
	{

		if (path.back() != '/')
			path += '/';
		if (sort == -1)
		{
			sort = handlers.rbegin()->id + 1;
		}
		if (sort > 1 << 15) //limit max sort id
			return false;

		RouterPoint rp;
		rp.id = sort;
		if (handlers.find(rp) != handlers.end())
			return false;
		rp.is_path = 1;
		rp.is_strict = is_strict;
		rp.url = std::move(url);
		rp.path = std::move(path);
		handlers.emplace(std::move(rp));
		return true;
	}

	bool Router::add_handler(std::string url, std::function<void(Request *, Reply *)> f, uint16_t sort, bool is_strict)
	{
		if (sort == -1)
		{
			sort = handlers.rbegin()->id + 1;
		}
		if (sort > 1 << 15)
			return false;
		RouterPoint rp;
		rp.id = sort;
		if (handlers.find(rp) != handlers.end())
			return false;
		rp.is_path = 0;
		rp.is_strict = is_strict;
		rp.url = std::move(url);
		rp.handler = std::move(f);
		handlers.emplace(std::move(rp));
		return true;
	}

	const RouterPoint *Router::find_handler(const char *buf, int len)
	{
		CString cs(buf, buf + len);
		for (auto &i : handlers)
		{
			if (i.is_strict)
			{
				if (cs == i.url)
				{
					return &i;
				}
			}
			else if (cs.start_with(i.url))
			{
				return &i;
			}
		}

		return nullptr;
	}
	/**
 * this function will be called by http service.
*/
	void Router::handle(Request *req, Reply *reply)
	{
		reply->startline_buf=req->make_reply_line();
		
		if (req->status != Status::OK)
		{
			reply->head_buf=reply->make_head_buf();
			return;
		
		}
		auto h = find_handler(req->url.data(), req->url.size());
		struct iovec content;
		content.iov_base=nullptr;
		if (h == nullptr)
		{
			reply->status = Status::NotFound; // url not valid;
			content=reply->make_buf(std::string("not found"));
		}
		else if (h->is_path) //is static file handler
		{
			std::filesystem::path p(h->path);
			if (h->is_strict == 0)
				p += req->url.substr(h->url.size());
			if (std::filesystem::exists(p))
			{
				int filesize = std::filesystem::file_size(p);
				if (filesize <= MAX_CONTENT_SIZE)
				{	
					content.iov_base = malloc(filesize);
					content.iov_len = filesize;

					reply->set_head("Content-Type", detect_filetype(p));
					std::ifstream file;
					file.open(p, std::ios::binary | std::ios::in);
					file.read((char *)(content.iov_base), content.iov_len);
					file.close();
					
				}
				else
				{

					auto s = "file too large. Max size : " + std::to_string(MAX_CONTENT_SIZE / 1024000) + "MB";
					content=reply->make_buf(std::move(s));
				}
				reply->status = Status::OK;
			}
			else
			{
				reply->status = Status::NotFound;
				content=reply->make_buf(std::string("static file not found."));
			}
		}
		else //is controller function handler
		{
			reply->status = Status::OK;
			h->handler(req, reply);
		}
		if(content.iov_base!=nullptr)
			reply->content_buf.push_back(content);
		reply->head_buf=reply->make_head_buf(); //create head

		return;
	}

}